//
//  j3RenderingEngine1.cpp
//  HJC3D
//
//  Created by HJC on 11-10-10.
//  Copyright 2011年 __MyCompanyName__. All rights reserved.
//

#include "j3RenderingEngine1.h"
using namespace std;

static const float AnimationDuration = 1;

extern j3RenderingEngine* CreateRenderer1()
{
    return new j3RenderingEngine1();
}



j3RenderingEngine1::j3RenderingEngine1()
{
    glGenRenderbuffersOES(1, &_colorRenderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, _colorRenderbuffer);
}



void j3RenderingEngine1::initConeData()
{
    const float coneRadius = 0.5f;
    const float coneHeight = 1.866f;
    const int coneSlices = 40;
    
    {
        // Allocate space for the cone vertices.
        _cone.resize((coneSlices + 1) * 2);
        
        // Initialize the vertices of the triangle strip.
        std::vector<Vertex>::iterator vertex = _cone.begin();
        const float dtheta = 2 * M_PI / coneSlices;
        for (float theta = 0; vertex != _cone.end(); theta += dtheta) {
            
            // Grayscale gradient
            float brightness = std::abs(std::sin(theta));
            vec4 color(brightness, brightness, brightness, 1);
            
            // Apex vertex
            vertex->Position = vec3(0, 1, 0);
            vertex->Color = color;
            vertex++;
            
            // Rim vertex
            vertex->Position.x = coneRadius * cos(theta);
            vertex->Position.y = 1 - coneHeight;
            vertex->Position.z = coneRadius * sin(theta);
            vertex->Color = color;
            vertex++;
        }
    }
    
    {
        // Allocate space for the disk vertices.
        _disk.resize(coneSlices + 2);
        
        // Initialize the center vertex of the triangle fan.
        std::vector<Vertex>::iterator vertex = _disk.begin();
        vertex->Color = vec4(0.75, 0.75, 0.75, 1);
        vertex->Position.x = 0;
        vertex->Position.y = 1 - coneHeight;
        vertex->Position.z = 0;
        vertex++;
        
        // Initialize the rim vertices of the triangle fan.
        const float dtheta = 2 * M_PI / coneSlices;
        for (float theta = 0; vertex != _disk.end(); theta += dtheta) {
            vertex->Color = vec4(0.75, 0.75, 0.75, 1);
            vertex->Position.x = coneRadius * cos(theta);
            vertex->Position.y = 1 - coneHeight;
            vertex->Position.z = coneRadius * sin(theta);
            vertex++;
        }
    }
}


void j3RenderingEngine1::initialize(int width, int height)
{
    this->initConeData();
      
    // Create the depth buffer.
    glGenRenderbuffersOES(1, &_depthRenderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, _depthRenderbuffer);
    glRenderbufferStorageOES(GL_RENDERBUFFER_OES,
                             GL_DEPTH_COMPONENT16_OES,
                             width,
                             height);
    
    // Create the framebuffer object; attach the depth and color buffers.
    glGenFramebuffersOES(1, &_framebuffer);
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, _framebuffer);
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
                                 GL_COLOR_ATTACHMENT0_OES,
                                 GL_RENDERBUFFER_OES,
                                 _colorRenderbuffer);
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
                                 GL_DEPTH_ATTACHMENT_OES,
                                 GL_RENDERBUFFER_OES,
                                 _depthRenderbuffer);
    
    // Bind the color buffer for rendering.
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, _colorRenderbuffer);
    
    glViewport(0, 0, width, height);
    glEnable(GL_DEPTH_TEST);
    
    glMatrixMode(GL_PROJECTION);
    glFrustumf(-1.6f, 1.6, -2.4, 2.4, 5, 10);
    
    glMatrixMode(GL_MODELVIEW);
    glTranslatef(0, 0, -7);
}


void j3RenderingEngine1::render() const
{
    glClearColor(0.5f, 0.5f, 0.5f, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    
    mat4 rotation(_animation.Current.ToMatrix());
    glMultMatrixf(rotation.pointer());
    
    // Draw the cone.
    glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &_cone[0].Position.x);
    glColorPointer(4, GL_FLOAT, sizeof(Vertex),  &_cone[0].Color.x);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, _cone.size());
    
    // Draw the disk that caps off the base of the cone.
   glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &_disk[0].Position.x);
   glColorPointer(4, GL_FLOAT, sizeof(Vertex), &_disk[0].Color.x);
   glDrawArrays(GL_TRIANGLE_FAN, 0, _disk.size());
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
    
    glPopMatrix();
}



float j3RenderingEngine1::rotationDirection() const
{
    float delta = _desiredAngle - _currentAngle;
    if (delta == 0)
        return 0;
    
    bool counterclockwise = ((delta > 0 && delta <= 180) || (delta < -180));
    return counterclockwise ? +1 : -1;
}



void j3RenderingEngine1::updateAnimation(float timeStep)
{
    if (_animation.Current == _animation.End)
        return;
    
    _animation.Elapsed += timeStep;
    if (_animation.Elapsed >= AnimationDuration) {
        _animation.Current = _animation.End;
    } else {
        float mu = _animation.Elapsed / AnimationDuration;
        _animation.Current = _animation.Start.Slerp(mu, _animation.End);
    }
}


void j3RenderingEngine1::onRotate(DeviceOrientation newOrientation)
{
    vec3 direction;
    
    switch (newOrientation) {
        case DeviceOrientationUnknown:
        case DeviceOrientationPortrait:
            direction = vec3(0, 1, 0);
            break;
            
        case DeviceOrientationPortraitUpsideDown:
            direction = vec3(0, -1, 0);
            break;
            
        case DeviceOrientationFaceDown:       
            direction = vec3(0, 0, -1);
            break;
            
        case DeviceOrientationFaceUp:
            direction = vec3(0, 0, 1);
            break;
            
        case DeviceOrientationLandscapeLeft:
            direction = vec3(+1, 0, 0);
            break;
            
        case DeviceOrientationLandscapeRight:
            direction = vec3(-1, 0, 0);
            break;
    }
    
    _animation.Elapsed = 0;
    _animation.Start = _animation.Current = _animation.End;
    _animation.End = Quaternion::CreateFromVectors(vec3(0, 1, 0), direction);
}



void j3RenderingEngine1::onFingerUp(ivec2 location)
{
    
}


void j3RenderingEngine1::onFingerDown(ivec2 location)
{
    
}


void j3RenderingEngine1::onFingerMove(ivec2 oldLocation, ivec2 newLocation)
{
    
}